#include <cassert>
#include "lock.hpp"
#include "rw_mutex.hpp"

using namespace vpm::utils;

RWMutex::RWMutex()
:   Lockable(),
mReaderCount(0),
mWriterHasLock(false),
mPendingWriterCount(0)
{
}


RWMutex::~RWMutex()
{
}


void
RWMutex::lock_read()
{
	Lock    lock(mMutex);

	while ( mWriterHasLock || mPendingWriterCount > 0 )
	{
		mReadCondition.wait(mMutex);
	}

	mReaderCount++;
}


void
RWMutex::lock_write()
{
	Lock  lock(mMutex);

	mPendingWriterCount++;

	while ( mWriterHasLock || mReaderCount > 0 )
	{
		mPendingWriteCondition.wait(mMutex);
	}

	mPendingWriterCount--;

	mWriterHasLock = true;
}


void
RWMutex::lock()
{
	lock_write();
}


void
RWMutex::unlock()
{
	Lock    lock(mMutex);

	// Unlocking a write lock.
	//
	if ( mWriterHasLock )
	{
		assert( mReaderCount == 0 );

		mWriterHasLock = false;

		// Pending writers have priority. Could potentially starve readers.
		//
		if ( mPendingWriterCount > 0 )
		{
			mPendingWriteCondition.signal();
		}

		// No writer, no pending writers, so all the readers can go.
		//
		else
		{
			mReadCondition.broadcast();
		}

	}

	// Unlocking a read lock.
	//
	else
	{
		assert( mReaderCount > 0 );

		mReaderCount--;

		if ( mReaderCount == 0 && mPendingWriterCount > 0 )
		{
			mPendingWriteCondition.signal();
		}
	}
}

unsigned int
RWMutex::reader_count() const
{
	return ( mReaderCount );
}

unsigned int
RWMutex::pending_writer_count() const
{
	return ( mPendingWriterCount );
}
